﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
using Asol.Reporting.Support;
using Asol.Reporting.Support.Data;
using Asol.Reporting.Editor.Editor;
using Asol.Reporting.Editor.Components;
using Asol.Reporting.Editor.Forms;
using Asol.Reporting.Editor.ComponentsLayer;
using PdfSharp.Drawing;
using PdfSharp.Pdf;

namespace Asol.Reporting.Editor.Data
{
    /// <summary>
    /// Datová obálka editovaného dokumentu. 
    /// Ta se ukládá do XML dat, tu lze renderovat do sestavy.
    /// Slouží jako zdroj/cíl dat pro editaci.
    /// </summary>
    public partial class ReportDocument : IRevertContainer
    {
        #region Document Preview, Print, Editor
        /// <summary>
        /// Ukončí preview v okně editoru, vrátí stav Editor
        /// </summary>
        private void CmdDocumentEditor()
        {
            if (this.HasForm)
                this.Form.ShowEditor();
        }
        /// <summary>
        /// Zajistí náhled na dokument.
        /// Vygeneruje sestavu do temp PDF a to zobrazí.
        /// </summary>
        private void CmdDocumentPreview()
        {
            using (PrintReportDocument printDocument = this.PrintDocumentCreate())
            {
                printDocument.RenderDocument(ReportParamValue.Edit);
                this.PdfContentView(printDocument);
            }
        }
        /// <summary>
        /// Zobrazí obsah dokumentu ve formuláři editoru, nebo v novém formuláři pro PDF náhled
        /// </summary>
        /// <param name="printDocument"></param>
        private void PdfContentView(PrintReportDocument printDocument)
        {
            byte[] content = printDocument.GetPdfContent();
            if (this.HasForm)
            {
                this.Form.ShowPdfContent(content);
            }
            else
            {
                using (PdfPreviewForm pv = new PdfPreviewForm())
                {
                    pv.LoadContent(content, this.DocumentFile);
                    pv.ShowDialog();
                }
            }
        }
        /// <summary>
        /// Otevře PDF dokument v přidružené aplikaci Windows
        /// </summary>
        private void PrintOpenDocument(PrintReportDocument printDocument)
        {
            string fileName = this.DocumentFile;
            byte[] content = printDocument.GetPdfContent();

            string tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "acro");
            if (!System.IO.Directory.Exists(tempPath))
                System.IO.Directory.CreateDirectory(tempPath);
            string tempFile = System.IO.Path.Combine(tempPath, System.IO.Path.GetFileNameWithoutExtension(fileName) + ".pdf");
            System.IO.File.WriteAllBytes(tempFile, content);

            System.Diagnostics.Process.Start(tempFile);
        }
        #endregion
        #region Příprava dokumentu
        /// <summary>
        /// Vytvoří, naplní a vrátí dokument
        /// </summary>
        /// <returns></returns>
        private PrintReportDocument PrintDocumentCreate()
        {
            PrintReportDocument document = new PrintReportDocument(this.RootBand.ReportData, this);

            // Do PrintReportDocument uložit data z this (bandy a komponenty): postavit strom vnořených Bands, seznamy Headers atd:
            IndexMulti<int, ReportBand> bandIndex = new IndexMulti<int, ReportBand>(b => b.OwnerBandId, this.BandDict.Values);
            IndexMulti<int, ReportComponent> componentIndex = new IndexMulti<int, ReportComponent>(c => c.OwnerTopBandId, this.ComponentDict.Values);
            List<ReportBand> level1 = bandIndex[this.RootBand.BandId];
            foreach (ReportBand band in level1)
            {

            }

            return document;
        }
        /// <summary>
        /// Z dodaného soupisu ReportBand vyfiltruje ty, jejichž OwnerBandId je rovno požadovanému.
        /// Tyto prvky odebere ze seznamu bands, a jejich oddělený soupis vrátí.
        /// </summary>
        /// <param name="bands"></param>
        /// <param name="ownerId"></param>
        /// <returns></returns>
        private List<ReportBand> PrintDocumentFilterBand(List<ReportBand> bands, int ownerId)
        {
            List<ReportBand> result = bands.Where(b => b.OwnerBandId == ownerId).ToList();
            bands.RemoveAll(b => b.OwnerBandId == ownerId);
            return result;
        }
        #endregion
  

        //private void RenderPageToGraphics(SimpleDataTable table)
        //{
        //    XGraphics gfx = this.PrintGraphics;
        //    // Jednobarevný rectangle:
        //    XBrush brush = new XSolidBrush(XColor.FromArgb(Color.LightGreen));
        //    RectangleF recArea = new RectangleF(55f, 22f, 40f, 40f);
        //    gfx.DrawRectangle(brush, recArea);
        //}
    }
    /// <summary>
    /// Tisková osnova dokumentu - základna reportu.
    /// Obsahuje objekt ReportTitle, sadu PageHeader a PageFooters a objekt ReportSummary, a sadu Groups (může popisovat i jeden detail).
    /// Obsahuje objekty pro tisk PDF.
    /// </summary>
    internal class PrintReportDocument : PrintReportData, IDisposable
    {
        #region Konstrukce
        public PrintReportDocument(ReportData reportData, ReportDocument owner)
            : base(reportData)
        {
            this.Owner = owner;
            this.InitData();
            this.InitPdfDocument();
        }
        /// <summary>
        /// Vlastník = dokument
        /// </summary>
        protected ReportDocument Owner { get; private set; }
        void IDisposable.Dispose()
        {
            this.DisposePdfDocument();
            this.Owner = null;
        }
        #endregion
        #region Části sestavy
        internal PrintReportBand ReportTitle { get; private set; }
        internal List<PrintReportBand> PageHeaders { get; private set; }
        internal List<PrintReportGroup> Groups { get; private set; }
        internal List<PrintReportBand> PageFooters { get; private set; }
        internal PrintReportBand ReportSummary { get; private set; }
        /// <summary>
        /// Inicializuje data reportu
        /// </summary>
        internal void InitData()
        {
            this.ReportTitle = new PrintReportBand();
            this.PageHeaders = new List<PrintReportBand>();
            this.Groups = new List<PrintReportGroup>();
            this.PageFooters = new List<PrintReportBand>();
            this.ReportSummary = new PrintReportBand();
        }
        #endregion
        #region PDF objekty (dokument, stránka, grafika, pozice, číslo strany atd)
        internal PdfDocument PdfDocument;
        internal PdfPage PdfPage;
        internal XGraphics PdfPageGraphics;
        internal float PagePositionY;
        internal int PageNumber;

        /// <summary>
        /// Připraví nový dokument. Volá se jedenkrát.
        /// </summary>
        protected void InitPdfDocument()
        {
            this.PdfDocument = new PdfDocument();
            this.PageNumber = 0;
        }
        /// <summary>
        /// Uvolní PDF dokument
        /// </summary>
        protected void DisposePdfDocument()
        {
            if (this.PdfPageGraphics != null) this.PdfPageGraphics.Dispose();
            if (this.PdfDocument != null) this.PdfDocument.Dispose();
        }
        /// <summary>
        /// Připraví novou stránku
        /// </summary>
        protected void PrepareNewPage()
        {
            this.PdfPage = this.PdfDocument.AddPage();
            this.PdfPage.Size = PdfSharp.PageSize.A4;
            this.PdfPage.Orientation = PdfSharp.PageOrientation.Portrait;

            this.PagePositionY = 0f;
            this.PageNumber++;

            this.PdfPageGraphics = XGraphics.FromPdfPage(this.PdfPage);
        }
        /// <summary>
        /// Vrátí aktuální obsah dokumentu
        /// </summary>
        /// <returns></returns>
        public byte[] GetPdfContent()
        {
            byte[] content = null;
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                this.PdfDocument.Save(ms);
                content = ms.GetBuffer();
            }
            return content;
        }
        #endregion
        #region Renderování dokumentu
        /// <summary>
        /// Provede renderování dokumentu
        /// </summary>
        /// <param name="paramValue"></param>
        internal void RenderDocument(ReportParamValue paramValue)
        {
            this.PrepareNewPage();
            this.PdfPageGraphics.DrawString("První string", new XFont("Arial", 14d), new XSolidBrush(Color.DarkBlue), new PointF(100f, 100f));
        }
        #endregion
    }
    internal class PrintReportGroup : PrintReportData
    {
        internal PrintReportGroup(ReportData reportData)
            : base(reportData)
        {
            this.InitData();
        }
        internal List<PrintReportBand> GroupHeaders { get; private set; }
        internal List<PrintReportBand> Details { get; private set; }
        internal List<PrintReportGroup> SubGroups { get; private set; }
        internal List<PrintReportBand> GroupFooters { get; private set; }
        /// <summary>
        /// Inicializuje data grupy
        /// </summary>
        private void InitData()
        {
            this.GroupHeaders = new List<PrintReportBand>();
            this.Details = new List<PrintReportBand>();
            this.SubGroups = new List<PrintReportGroup>();
            this.GroupFooters = new List<PrintReportBand>();
        }
    }
    internal class PrintReportBand
    {
        public List<ReportComponent> Components { get; private set; }
    }
    /// <summary>
    /// Základní třída pro třídy tisku sestavy, která může obsahovat vlastní zdroj dat
    /// </summary>
    internal class PrintReportData
    {
        internal PrintReportData(ReportData reportData)
        {
            this.ReportData = reportData;
        }
        internal ReportData ReportData { get; set; }
    }
}
